React |
您所在的位置:网站首页 › react suspense解决异步问题 › React |
前言
React有三种不同的模式,concurent模式、legacy模式,以及处于concurrent和legacy之间的过渡模式blocking。 这里主要讲下state 处于 Concurrent模式与 Legacy模式 差异性。 大概对应的React版本号: 模式版本ConcurrentReact18以及之后的版本LegacyReact17以及之前的版本React未来的版本都将采用 Concurrent 模式,其采用渐进增强的方式向后兼容。 以下皆在函数组件举例,state的行为 类组件与函数组件 中表现一致。 legacy 模式 在钩子函数与合成事件中,state的表现是:批量的、异步的 import { Button } from "antd" import React, { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1) console.log('111--', count) setCount(2) console.log('222--', count) setCount(3) console.log('333--', count) } console.log('render --', count) return ( test ) }打印的结果: 批量的:调用了三次setCount,但是最后函数组件只渲染一次(只打印一次 render ---3),也就是将批量的更新合并成一次并进行更新的。 异步的:同时打印了三次 count 并且count值打印三次都是0,这是因为函数组件的更新,本质上是重新执行了一次函数,也就是说通过setCount更新的count值,需要在下次函数执行的时候,可以拿到更新后的值。 在异步操作(setTimeout、setInterval)中,state的表现是:非批量的、同步的 import { Button } from "antd" import React, { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }); } console.log('render --', count) return ( test ) }打印结果: 打印结果可以看到,代码是同步执行的,并且每执行一次setCount,都会更新一次函数组件, 也就是说更新是非批量的 unstable_batchedUpdates在异步操作中,可以使用 unstable_batchedUpdates 实现手动批量更新。 import { Button } from "antd" import React, { useState } from "react" import { unstable_batchedUpdates } from "react-dom" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { unstable_batchedUpdates(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }) }); } console.log('render --', count) return ( test ) }打印结果: 可以看到,在引入了 unstable_batchedUpdates 改造异步操作的代码,setCount从非批量、同步 变成了 批量、异步。 场景unstable_batchedUpdates 有一个经常遇到的使用场景,那就是在发起请求,获取结果后需要更新多次的state import { Button } from "antd"; import React, { useState } from "react"; export const Index = () => { const [count, setCount] = useState(0); const [name, setName] = useState(""); const [age, setAge] = useState(18); const getUserInfoHttp = () => { return new Promise((resolve) => { setTimeout(() => { const obj = { name: "jack", age: 25, pageCount: 21, data: [] }; resolve(obj); }, 1000); }); }; const handleClick = () => { getUserInfoHttp().then((res) => { setName(res?.name); setAge(res?.age); setCount(res?.pageCount); }); }; console.log("render name--", name); console.log("render age--", age); console.log("render count--", count); return ( test ); };打印结果: 可以看到打印了三次,也就是页面刷新太频繁了,可以使用 unstable_batchedUpdates 做下优化 import { Button } from "antd"; import React, { useState } from "react"; import { unstable_batchedUpdates } from "react-dom"; export const Index = () => { const [count, setCount] = useState(0); const [name, setName] = useState(""); const [age, setAge] = useState(18); // 发起请求获取信息 const getUserInfoHttp = () => { return new Promise((resolve) => { setTimeout(() => { const obj = { name: "jack", age: 25, pageCount: 21, data: [] }; resolve(obj); }, 1000); }); }; const handleClick = () => { getUserInfoHttp().then((res) => { // 批量更新优化 unstable_batchedUpdates(() => { setName(res?.name); setAge(res?.age); setCount(res?.pageCount); }); }); }; console.log("render name--", name); console.log("render age--", age); console.log("render count--", count); return ( test ); };打印结果: 使用了 unstable_batchedUpdates 优化之后,页面只刷新了一次 concurrent 模式不管是在异步操作中还是在合成事件中,state都表现出:批量的、异步的 1. 合成事件中 import { Button } from "antd" import { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1) console.log('111--', count) setCount(2) console.log('222--', count) setCount(3) console.log('333--', count) } console.log('render --', count) return ( test ) }打印结果: 2. 在异步操作中 import { Button } from "antd" import { useState } from "react" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setTimeout(() => { setCount(1); console.log("count读取的是闭包值 111--", count); setCount(2); console.log("count读取的是闭包值 222--", count); setCount(3); console.log("count读取的是闭包值 333--", count); }); } console.log('render --', count) return ( test ) }打印结果: flushSync 可以将回调函数中的更新任务,放在一个较高的优先级中 使用flushSync 设置优先级 import { Button } from "antd" import { useState } from "react" import { flushSync } from "react-dom" export const Index = () => { const [count, setCount] = useState(0) const handleClick = () => { setCount(1); // 设置优先级 flushSync(() =>{ setCount(2); console.log("count 111--", count); }) setCount(3); } console.log('render --', count) return ( test ) }打印结果: 可以看到,先更新了setCount(2),后更新setCount(3) 结论state 的同步/异步问题,需要看场景、React当前模式 1.Legacy 模式下 a.在钩子函数和合成事件中,state表现为批量的、异步的 b.在异步操作中(setTimeout、Promise),state表现为非批量的、同步的 2.Concurrent 模式下 state表现都是 批量的、异步的 useState 需要注意的一些问题 浅比较 const [userInfo, setUserInfo] = useState({}) const copyData = { ...userInfo } // 进行浅拷贝 重新分配内存空间 copyData.name = 'jack' setUserInfo(copyData)以下是无效的写法: const [userInfo, setUserInfo] = useState({}) userInfo.name = 'jack' setUserInfo(userInfo) // 不会更新 因为浅比较的结果是,值userInfo没有发生变化原因分析:在进行setState更新值的时候,React会进行一次浅比较,也就是当前更新的值和上一次的值,是否有发生变化,如果值没有发生变化,那么不进行更新,否则进行更新。浅比较是进行内存地址的比较,因此直接在原来的值上进行修改,相当于没有变化 获取最新值给 useState 的 dispatch 传递的参数是一个函数,既可获取修改后的值 const [count, setCount] = useState(0) setCount(value => value + 1) // count = 0 + 1 setCount(value => value + 1) // count = 1 + 1 setCount(value => value + 1) // count = 2 + 1 参考文献React 的 setState 是同步还是异步 setState的执行机制 React useState和setState到底是同步还是异步 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |